package com.quick.develop.flowable.framework.cmd;

import cn.hutool.core.collection.CollUtil;
import com.quick.develop.flowable.constant.BpmConstants;
import com.quick.develop.flowable.enums.BpmExecuteCmd;
import com.quick.develop.framework.utils.StringUtils;
import com.quick.develop.framework.security.utils.SecurityUtils;
import com.quick.develop.flowable.utils.FindNextNodeUtil;
import com.quick.develop.flowable.utils.FlowableUtil;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.*;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.persistence.entity.CommentEntity;
import org.flowable.engine.impl.persistence.entity.CommentEntityManager;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.runtime.ActivityInstance;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.flowable.task.service.HistoricTaskService;
import org.flowable.task.service.impl.persistence.entity.HistoricTaskInstanceEntity;

import java.io.Serializable;
import java.util.*;

import static com.quick.develop.flowable.enums.BpmErrorCodeConstants.*;
import static com.quick.develop.framework.common.utils.ServiceExceptionUtils.exception;
import static com.quick.develop.framework.utils.collection.CollectionUtils.convertList;
import static com.quick.develop.framework.utils.collection.CollectionUtils.convertSet;

/**
 * @author junyuan.liu
 * @description:
 * @date 2022/9/12 10:39
 */
public class WithdrawTaskCmd implements Command<Void>, Serializable {
    private static final long serialVersionUID = -5407771003938182812L;


    protected String taskId;
    protected String message;
    protected String businessKey;
    public WithdrawTaskCmd(String taskId, String message, String businessKey) {
        this.taskId = taskId;
        this.message = message;
        this.businessKey = businessKey;
    }

    @Override
    public Void execute(CommandContext commandContext) {
        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
        TaskService taskService = processEngineConfiguration.getTaskService();
        RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
        ManagementService managementService = processEngineConfiguration.getManagementService();
        HistoricTaskService historicTaskService = CommandContextUtil.getHistoricTaskService();
        HistoricTaskInstanceEntity hisTask = historicTaskService.getHistoricTask(taskId);

        if (canWithdraw(hisTask, processEngineConfiguration)) {
            List<ActivityInstance> list = runtimeService.createActivityInstanceQuery()
                    .processInstanceId(hisTask.getProcessInstanceId()).unfinished().list();
            Optional.ofNullable(list)
                    .orElseThrow(() -> exception(TASK_WITHDRAW_FAIL_EXECUTION_IS_EMPTY));
            List<String> actIds = convertList(list, ActivityInstance::getActivityId);
            // Set<String> idsSet = convertSet(list, Execution::getActivityId);

            // List<String> executionIds = new ArrayList<>();
            String userCode = SecurityUtils.getUserCode();
            list.forEach(e -> {
                // executionIds.add(e.getId());
                if (StringUtils.isNotEmpty(e.getTaskId())) {
                    org.flowable.task.api.Task task = taskService.createTaskQuery().taskId(e.getTaskId()).singleResult();
                    // 被撤回任务设置归属人
                    Optional.ofNullable(task)
                            .ifPresent(t -> {
                                String id = t.getId();
                                if (StringUtils.isNotEmpty(t.getAssignee())) {
                                    taskService.setOwner(id, t.getAssignee());
                                }
                                // 被撤回的任务设置 处理人为当前人
                                if (StringUtils.isEmpty(t.getAssignee())) {
                                    taskService.setVariableLocal(id, BpmConstants.ENTRUST_IGNORE, BpmConstants.ENTRUST_IGNORE);
                                    taskService.setAssignee(id, userCode);
                                }
                                // taskService.setVariableLocal(id, BpmConstants.EXECUTE_CMD, BpmExecuteCmd.WITHDRAW.cmd);
                                managementService.executeCommand(new SetTaskScopeTypeCmd(id, BpmExecuteCmd.WITHDRAW.cmd));
                                // 被撤回的任务增加 意见
                                addComment(commandContext, t.getProcessInstanceId(), id, hisTask.getName());
                            });
                }
            });
            if (CollUtil.isEmpty(actIds)) {
                throw exception(TASK_WITHDRAW_FAIL_EXECUTION_IS_EMPTY);
            }
            //设置标识
            runtimeService.setVariable(hisTask.getProcessInstanceId(), BpmConstants.EXECUTE_CMD, BpmExecuteCmd.WITHDRAW.cmd);
            runtimeService.setVariable(hisTask.getProcessInstanceId(), BpmConstants.WITHDRAW_USER, userCode);
            runtimeService.createChangeActivityStateBuilder()
                    .processInstanceId(hisTask.getProcessInstanceId())
                    .moveActivityIdsToSingleActivityId(actIds, hisTask.getTaskDefinitionKey())
                    .changeState();
            this.withdrawTask();

        }

        return null;
    }

    private void withdrawTask() {
        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
        HistoryService historyService = processEngineConfiguration.getHistoryService();
        TaskService taskService = processEngineConfiguration.getTaskService();
        HistoricTaskInstanceQuery historicTaskInstanceQuery = historyService.createHistoricTaskInstanceQuery();
        HistoricTaskInstance taskInstance = historicTaskInstanceQuery.taskId(taskId).singleResult();
        if (Objects.isNull(taskInstance)) {
            return;
        }
        String processInstanceId = taskInstance.getProcessInstanceId();
//        List<HistoricTaskInstance> taskInstances = historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId).unfinished().list();
//        List<HistoricTaskInstance> historicTaskInstances = Optional.ofNullable(taskInstances).orElse(new ArrayList<>());
//        List<String> activeIds = historicTaskInstances.stream().map(HistoricTaskInstance::getId).collect(Collectors.toList());

    }

    private Boolean canWithdraw(HistoricTaskInstanceEntity hisTask, ProcessEngineConfigurationImpl processEngineConfiguration) {
        Optional.ofNullable(hisTask)
                .orElseThrow(() -> exception(TASK_WITHDRAW_FAIL_TASK_NOT_EXISTS));
        TaskService taskService = processEngineConfiguration.getTaskService();
        HistoryService historyService = processEngineConfiguration.getHistoryService();
        RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
        RepositoryService repositoryService = processEngineConfiguration.getRepositoryService();
        //流程已经结束
        if (isProcessFinished(historyService, hisTask.getProcessInstanceId())) {
            throw exception(TASK_WITHDRAW_FAIL_PROCESS_FINISHED);
        }
        // 当前节点处于callActivity 且未完成 处于子流程中
        if (!isCallActivityFinished(runtimeService, hisTask.getProcessInstanceId())) {
            throw exception(TASK_WITHDRAW_FAIL_CALL_ACTIVITY_UNFINISHED);
        }
        BpmnModel bpmnModel = repositoryService.getBpmnModel(hisTask.getProcessDefinitionId());

        // 撤回的节点为会签节点 不让撤回
        if (FlowableUtil.isTaskMulti(bpmnModel, hisTask.getTaskDefinitionKey())) {
            throw exception(TASK_WITHDRAW_FAIL_TASK_IS_MULTI);
        }
        // 正在运行的任务
        List<org.flowable.task.api.Task> list = taskService.createTaskQuery()
                .processInstanceId(hisTask.getProcessInstanceId()).list();
        Set<String> keySet = convertSet(list, org.flowable.task.api.Task::getTaskDefinitionKey);

        // 运行的节点是否处于会签节点

        // 当前运行节点是否是 撤回节点的 下一个节点
        if (keySet.isEmpty() || !runTaskIsNext(keySet, bpmnModel, hisTask)) {
            throw exception(TASK_WITHDRAW_FAIL_TASK_IS_APPROVE);
        }

        // 撤回的节点 有运行任务
        long count = taskService.createTaskQuery()
                .processInstanceId(hisTask.getProcessInstanceId())
                .taskDefinitionKey(hisTask.getTaskDefinitionKey())
                .count();
        if (count > 0) {
            throw exception(TASK_WITHDRAW_FAIL_TASK_IS_CURRENT);
        }

        return true;
    }

    private boolean isProcessFinished(HistoryService historyService, String processInstanceId) {
        return historyService.createHistoricProcessInstanceQuery().finished()
                .processInstanceId(processInstanceId).count() > 0;
    }

    private boolean isCallActivityFinished(RuntimeService runtimeService, String processInstanceId) {
        return runtimeService.createActivityInstanceQuery()
                .activityType(BpmConstants.CALL_ACTIVITY)
                .processInstanceId(processInstanceId)
                .unfinished()
                .count() == 0;
    }


    private boolean runTaskIsNext(Set<String> keySet, BpmnModel bpmnModel, HistoricTaskInstanceEntity hisTask) {
        boolean flag = true;
        // 撤回节点的下一个节点集合 不包含调用活动
        Set<String> nextIds = getNextNode(bpmnModel, hisTask.getTaskDefinitionKey());
        for (String s : keySet) {
            if (!nextIds.contains(s)) {
                flag = false;
                break;
            }
        }

        return flag;
    }

    /**
     * @param bpmnModel         BpmnModel
     * @param taskDefinitionKey 节点key
     * @return java.util.List<java.lang.String>
     * @description 获取下一个节点结合
     * @author junyuan.liu
     * @date 2022/9/12 13:19
     */
    private Set<String> getNextNode(BpmnModel bpmnModel, String taskDefinitionKey) {
        List<UserTask> data = new ArrayList<>();
        Process mainProcess = bpmnModel.getMainProcess();
        Collection<FlowElement> flowElements = mainProcess.getFlowElements();
        FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey);
        next(flowElements, flowElement, data);

        return convertSet(data, UserTask::getId);
    }

    private void next(Collection<FlowElement> flowElements, FlowElement flowElement, List<UserTask> data) {
        //如果是结束节点
        if (flowElement instanceof EndEvent) {
            //如果是子任务的结束节点
            if (FindNextNodeUtil.getSubProcess(flowElements, flowElement) != null) {
                flowElement = FindNextNodeUtil.getSubProcess(flowElements, flowElement);
            } else {
                UserTask userTask = new UserTask();
                userTask.setId(flowElement.getId());
                data.add(userTask);
            }
        }

        //获取Task的出线信息--可以拥有多个
        List<SequenceFlow> outGoingFlows = null;
        if (flowElement instanceof Task) {
            outGoingFlows = ((Task) flowElement).getOutgoingFlows();
        } else if (flowElement instanceof Gateway) {
            outGoingFlows = ((Gateway) flowElement).getOutgoingFlows();
        } else if (flowElement instanceof StartEvent) {
            outGoingFlows = ((StartEvent) flowElement).getOutgoingFlows();
        } else if (flowElement instanceof SubProcess) {
            outGoingFlows = ((SubProcess) flowElement).getOutgoingFlows();
        } else if (flowElement instanceof CallActivity) {
            outGoingFlows = ((CallActivity) flowElement).getOutgoingFlows();
        }
        if (Objects.nonNull(outGoingFlows) && outGoingFlows.size() > 0) {
            //遍历所有的出线--找到所有的目标节点，不判断分支条件
            for (SequenceFlow outGoingFlow : outGoingFlows) {
                //出线的下一节点
                String nextFlowElementId = outGoingFlow.getTargetRef();
                if (FindNextNodeUtil.checkSubProcess(nextFlowElementId, flowElements, data)) {
                    continue;
                }
                //查询下一节点的信息
                FlowElement nextFlowElement = FindNextNodeUtil.getFlowElementById(nextFlowElementId, flowElements);
                //调用流程
                if (nextFlowElement instanceof CallActivity) {
                    CallActivity ca = (CallActivity) nextFlowElement;
                    if (ca.hasMultiInstanceLoopCharacteristics()) {
                        UserTask userTask = new UserTask();
                        userTask.setId(ca.getId());
                        userTask.setLoopCharacteristics(ca.getLoopCharacteristics());
                        data.add(userTask);
                    }
                }

                //用户任务
                if (nextFlowElement instanceof UserTask) {
                    data.add((UserTask) nextFlowElement);
                }
                //所有网关
                else if (nextFlowElement instanceof Gateway) {
                    next(flowElements, nextFlowElement, data);
                }
                //接收任务
                else if (nextFlowElement instanceof ReceiveTask) {
                    next(flowElements, nextFlowElement, data);
                }
                //服务任务
                else if (nextFlowElement instanceof ServiceTask) {
                    next(flowElements, nextFlowElement, data);
                }
                //子任务的起点
                else if (nextFlowElement instanceof StartEvent) {
                    next(flowElements, nextFlowElement, data);
                }
                //结束节点
                else if (nextFlowElement instanceof EndEvent) {
                    next(flowElements, nextFlowElement, data);
                }

            }
        }

    }

    private void addComment(CommandContext commandContext, String processInstanceId, String taskId, String txt) {
        String userCode = SecurityUtils.getUserCode();
        String username = SecurityUtils.getUsername();
        CommentEntityManager commentEntityManager = CommandContextUtil.getCommentEntityManager(commandContext);
        CommentEntity comment = (CommentEntity) commentEntityManager.create();
        comment.setProcessInstanceId(processInstanceId);
        comment.setUserId(userCode);
        comment.setType(BpmExecuteCmd.WITHDRAW.cmd);
        comment.setTime(CommandContextUtil.getProcessEngineConfiguration(commandContext).getClock().getCurrentTime());
        comment.setTaskId(taskId);
        comment.setAction(BpmConstants.ADD_COMMENT);
        StringBuilder eventMessage = new StringBuilder();
        eventMessage.append("由").append(username).append(BpmExecuteCmd.WITHDRAW.msg)
                .append("到：").append(txt);
        Optional.ofNullable(message)
                .ifPresent(c -> {
                    if (eventMessage.length() == 0) {
                        eventMessage.append(c);
                    } else {
                        eventMessage.append("；").append(c);
                    }
                });

        String fullStr = eventMessage.toString().replaceAll("\\s+", " ");

        comment.setMessage(fullStr.length() > 163 ? fullStr.substring(0, 160) + "..." : fullStr);
        comment.setFullMessage(fullStr);
        commentEntityManager.insert(comment);
    }
}
